import argparse
from time import sleep
import winreg
import json
import pyedid
from tkinter import (
    BOTH, DISABLED, END, NORMAL, W, WORD, X, Y, Text, Tk, Frame, Label,  Scrollbar, Button, Entry,
    messagebox, LEFT, RIGHT, TOP, BOTTOM
)
from tkinter.ttk import Progressbar, Treeview 


class EdidGUI:
    def __init__(self, auto=False, export=False):
        self.root = Tk()
        self.root.title("EDID信息查看器")
        self.root.geometry("1024x768")  # 设置固定窗口大小
        self.auto_validation = auto  # 新增自动验证标志
        self.auto_export = export          # 新增导出路径
         # 新增状态变量
        self.current_validation_index = 0  # 当前验证的设备索引
        self.last_validation_error = None  # 上次验证失败的设备
        
        # 创建主框架
        self.main_frame = Frame(self.root)
        self.main_frame.pack(fill=BOTH, expand=True)
        
        # 左侧设备列表
        self.left_frame = Frame(self.main_frame, width=250)
        self.left_frame.pack(side=LEFT, fill=Y)
        
        self.tree = Treeview(self.left_frame, columns=('ID',), show='headings')
        self.tree.heading('ID', text='设备ID')
        self.tree.column('ID', width=150)
        self.tree.pack(fill=BOTH, expand=True)
        
        # 右侧信息面板
        self.right_frame = Frame(self.main_frame)
        self.right_frame.pack(side=RIGHT, fill=BOTH, expand=True)
        
        # 添加底部按钮区域
        self.bottom_frame = Frame(self.root)
        self.bottom_frame.pack(side=BOTTOM, fill=X, pady=10)
        # 新增状态提示标签
        self.status_label = Label(self.bottom_frame, 
                                 text="验证状态：",
                                 font=('微软雅黑', 9),
                                 fg='#666666')
        self.status_label.pack(side=LEFT, padx=5)
        self.start_btn = Button(self.bottom_frame, 
                               text="开始验证", 
                               command=self.perform_batch_validation,
                               font=('微软雅黑', 12),
                               width=15,
                               bg='#4CAF50',
                               fg='white')
        self.start_btn.pack(pady=5)
        
        # 信息显示区域
        self.info_labels = {}
        self.validation_tabs = {}
        
        # 加载数据
        self.model_data = {}
        self.load_edid_data()
        
        # 布局管理
        self.root.columnconfigure(1, weight=3)  # 主列权重
        
    def export_report(self):
        """保存当前验证状态"""
        selected_items = self.tree.selection()
        if not selected_items:
            messagebox.showwarning("警告", "请先选择要验证的设备")
            return
        self.validation_in_progress = True
        selected_item = selected_items[0]
        model_name = self.tree.item(selected_item, 'values')[0]
        path = f"{model_name}.txt"
        try:
            with open(path, 'w') as f:
                json.dump(self.model_data[model_name], f)
            messagebox.showinfo("导出", f"成功导出设备{model_name}数据")
        except Exception as e:
            print(f"保存配置失败: {e}")
    def test_current(self):
        (model_name, status) = self.start_validation()
        if status:
            print(f"设备 {model_name} 验证通过")
        else:
            messagebox.showerror("验证结果", f"设备{model_name} 测试OK： {status}")
        
    def perform_batch_validation(self):
        """执行批量验证"""
        self.start_btn.config(state=DISABLED)
        count = len(self.tree.get_children())
        for i in range(count):
            self.tree.selection_set(self.tree.get_children()[i])
            sleep(0.2)
            (model_name, status) = self.start_validation()
            # 显示验证结果
            status_msg = f"设备 {model_name} 验证{'通过' if status else '失败'}"
            print(status_msg)
            if not status:
                messagebox.showerror("验证结果", status_msg)
                return False
        print("验证结果", f"所有设备测试OK： {count}")
        print("")
        data = {
            "status": True,
            "content": f"所有设备测试OK： {count}",
            "opts": self.model_data
        }
        print(f"R<{data}>R")
        if self.auto_export:
            with open("res.edid-pcba.txt", 'w') as f:
                json.dump(data, f)
        self.start_btn.config(state=NORMAL)
        self.on_close()
        return True
        
    def show_validation_summary(self, total, error_device):
        """显示验证总结弹窗"""
        title = "验证总结"
        if error_device is not None:
            message = f"共验证{total}个设备，其中1个失败。\n最后一次失败设备：{error_device}"
            messagebox.showerror(title, message)
        else:
            message = f"所有{total}个设备验证通过！"
            messagebox.showinfo(title, message)
    def refresh_device_list(self):
        """刷新设备列表显示"""
        self.tree.delete(*self.tree.get_children())
        for model in sorted(self.model_data.keys()):
            status = "✅" if self.model_data[model]['validation']['status'] else "❌"
            self.tree.insert('', END, values=(model, status))
    def start_validation(self):
        """开始按钮点击事件"""
        selected_items = self.tree.selection()
        if not selected_items:
            messagebox.showwarning("警告", "请先选择要验证的设备")
            return
        self.validation_in_progress = True
        selected_item = selected_items[0]
        model_name = self.tree.item(selected_item, 'values')[0]
        raw_hex = self.model_data[model_name]['raw_hex']  # 获取原始EDID数据
         # 执行验证逻辑
        validation_result = validate_edid(raw_hex)  # 这里调用独立的验证函数
        status = validation_result['status']
        steps = validation_result['steps']
         # 保存验证结果到模型数据
        self.model_data[model_name]['validation'] = {
            'status': status,
            'steps': steps
        }
        self.validation_in_progress = False
        # 刷新右侧面板的信息
        self.show_model_info(None)
        return (model_name, status)
        
    def run(self):
        """运行主循环"""
        self.root.protocol("WM_DELETE_WINDOW", self.on_close)
        self.tree.bind('<<TreeviewSelect>>', self.show_model_info)

        # 添加延迟执行
        if self.auto_validation:
            self.root.after(100, self.perform_batch_validation)
        
        self.root.mainloop()
    
    def on_close(self):
        """关闭窗口事件"""
        # if self.current_validation_index > 0 or self.last_validation_error is not None:
        #     confirm = messagebox.askyesno("确认退出", 
        #                                     "当前验证进度未保存，是否退出？")
        #     if not confirm:
        #         return
        self.root.destroy()
        
        
    def load_edid_data(self):
        """加载所有EDID数据并建立模型"""
        raw_list = get_edid_from_registry()
        unique_models = {}
        
        for raw in raw_list:
            try:
                edid_info = parse_data(raw)
                model_name = edid_info.get('id', '未知ID')
                if model_name not in unique_models:
                    unique_models[model_name] = {
                        'info': edid_info,
                        'raw_hex': raw,
                        'validation': {
                        'status': False,
                        'steps': []
                    }
                    }
            except Exception as e:
                print(f"解析EDID失败: {raw} - {e}")
        
        # 更新左侧列表
        self.tree.delete(*self.tree.get_children())
        for model in sorted(unique_models.keys()):
            self.tree.insert('', END, values=(model))
        if self.tree.get_children():
            self.tree.selection_set(self.tree.get_children()[0])
        # 建立数据映射
        self.model_data = {model: info for model, info in unique_models.items()}
        
    def show_model_info(self, _event):
        """显示选中模型的详细信息"""
        selected_items = self.tree.selection()
        if not selected_items:
            return
        
        # 获取选中项数据
        selected_item = selected_items[0]
        model_name = self.tree.item(selected_item, 'values')[0]
        info_dict = self.model_data[model_name]['info']
        validation_dict = self.model_data[model_name]['validation']
        validation_steps = validation_dict.get('steps', [])
        validation_passed = validation_dict.get('status', False)
        # 清空右侧面板
        for widget in self.right_frame.winfo_children():
            widget.destroy()

        # ===== 基本信息面板 =====
        info_frame = Frame(self.right_frame, padx=10, pady=5)
        info_frame.pack(fill=X, anchor='w')
        
        info_data = [
            ("制造商", info_dict.get('manufacturer', '未知')),
            ("型号", info_dict.get('model_name', '未知')),
            ("序列号", info_dict.get('serial_number', '无')),
            ("生产日期", f"Year {info_dict.get('year', '未知')} Week {info_dict.get('week', '未知')}"),
            ("物理尺寸", f"{info_dict.get('width', '0')}cm × {info_dict.get('height', '0')}cm"),
            ("EDID版本", info_dict.get('edid_version', '1.3'))
        ]

        for idx, (title, value) in enumerate(info_data):
            row = Frame(info_frame)
            row.grid(row=idx//2, column=idx%2, sticky='w', pady=2)
            Label(row, text=f"{title}:", font=('微软雅黑', 10, 'bold'), width=8).pack(side=LEFT)
            Label(row, text=value, font=('微软雅黑', 10)).pack(side=LEFT)

        # ===== 验证状态面板 =====
        status_frame = Frame(self.right_frame, 
                            relief="groove", 
                            borderwidth=2,
                            padx=10,
                            pady=5)
        status_frame.pack(fill=X, pady=10, padx=5)
        if len(validation_steps) == 0:
            status_icon = '🖐️'
            status_color = '#0aa0AA'
            status_txt = "等待"
        elif validation_passed:
            status_icon = '✅'
            status_color = '#4CAF50'
            status_txt = "通过"
        else:
            status_icon = '❌'
            status_color = '#F44336'
            status_txt = "失败"
        Label(status_frame, 
            text=f" {status_icon} 整体验证状态：{status_txt}",
            font=('微软雅黑', 12, 'bold'),
            fg=status_color).pack(anchor='w')

        # ===== 详细验证信息 =====
        main_panel = Frame(self.right_frame)
        main_panel.pack(fill=BOTH, expand=True)

        # 右侧详细信息
        detail_frame = Frame(main_panel)
        detail_frame.pack(side=LEFT, fill=BOTH, expand=True)

        # 带滚动条的详细信息文本框
        text_container = Frame(detail_frame, borderwidth=1, relief="sunken")
        text_container.pack(fill=BOTH, expand=True, padx=5, pady=5)

        scrollbar = Scrollbar(text_container)
        scrollbar.pack(side=RIGHT, fill=Y)

        info_text = Text(text_container,
                        wrap=WORD,
                        yscrollcommand=scrollbar.set,
                        font=('Consolas', 9),
                        padx=5,
                        pady=5)
        info_text.pack(fill=BOTH, expand=True)
        scrollbar.config(command=info_text.yview)

        # 插入格式化数据
        formatted_json = json.dumps(validation_dict, 
                                indent=2, 
                                ensure_ascii=False,
                                sort_keys=True)
        info_text.insert(END, formatted_json)
        info_text.config(state=DISABLED)  # 设为只读

        # 高亮错误步骤
        for idx, step in enumerate(validation_steps, start=1):
            if not step['status']:
                line_start = f"{idx}.0"
                line_end = f"{idx}.end"
                info_text.tag_add('error', line_start, line_end)
        
        info_text.tag_config('error', background='#FFEBEE', foreground='#B71C1C')
        # ===== 在验证信息下方添加操作按钮 =====
        action_frame = Frame(self.right_frame)
        action_frame.pack(side=BOTTOM, fill=X, pady=10)
        
        Button(action_frame, 
              text="导出报告", 
              command=self.export_report,
              width=10).pack(side=LEFT, padx=5)
        
        Button(action_frame,
              text="验证",
              command=self.test_current,
              width=10).pack(side=LEFT, padx=5)
        # 强制更新布局
        self.right_frame.update_idletasks()
    
    def highlight_step(self, steps):
        """高亮显示验证失败步骤"""
        self.validation_text.tag_remove("error", "1.0", END)
        for idx, step in enumerate(steps):
            if not step['status']:
                pos = f"1.{idx+1}.0"
                self.validation_text.tag_add("error", pos, f"{pos}lineend")
                self.validation_text.tag_config("error", background="pink", foreground="red")
    
    def clear_all(self):
        """清空所有显示信息"""
        for label in self.info_labels.values():
            label.config(text='')
        self.validation_progress['value'] = 0
        self.validation_status.config(text='')
        self.validation_text.delete(1.0, END)
        self.validation_text.tag_remove("error", "1.0", END)
        
    def show_error(self, message):
        """显示错误信息"""
        messagebox.showerror("错误", message)
        self.clear_all()
        
    def show_message(self, title, content):
        """显示普通消息"""
        messagebox.showinfo(title, content)
        self.clear_all()

def get_edid_from_registry():
    """从注册表获取EDID数据并去重"""
    path = r'SYSTEM\CurrentControlSet\Enum\DISPLAY'
    seen_edids = set()
    data = []

    try:
        with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as hkey:
            with winreg.OpenKey(hkey, path) as display_key:
                for i in range(winreg.QueryInfoKey(display_key)[0]):
                    device_name = winreg.EnumKey(display_key, i)
                    device_path = f'{path}\\{device_name}'
                    with winreg.OpenKey(hkey, device_path) as device_key:
                        for j in range(winreg.QueryInfoKey(device_key)[0]):
                            sub_device_name = winreg.EnumKey(device_key, j)
                            sub_device_path = f'{device_path}\\{sub_device_name}\\Device Parameters'
                            try:
                                with winreg.OpenKey(hkey, sub_device_path) as params_key:
                                    edid_value, _ = winreg.QueryValueEx(params_key, 'EDID')
                                    raw_edid_hex = edid_value.hex()
                                    
                                    if raw_edid_hex not in seen_edids:
                                        seen_edids.add(raw_edid_hex)
                                        data.append(raw_edid_hex)
                            except FileNotFoundError:
                                continue
    except Exception as e:
        print(f"读取注册表时出错: {e}")

    return data

def parse_data(raw_hex):
    """解析EDID数据并进行验证"""
    data = pyedid.parse_edid(raw_hex)
    
    return {
        "id": f"{data.manufacturer_pnp_id}{data.manufacturer_id}",
        "manufacturer_id": data.manufacturer_id,
        "manufacturer": data.manufacturer,
        "manufacturer_pnp_id": data.manufacturer_pnp_id,
        "model_name": data.name,
        "serial_number": data.serial,
        "product_id": data.product_id,
        "year": data.year,
        "week": data.week,
        "edid_version": data.edid_version,
        "type": data.type,
        "width": data.width,
        "height": data.height,
        "gamma": data.gamma,
        "dpms_standby": data.dpms_standby,
        "dpms_suspend": data.dpms_suspend,
        "dpms_activeoff": data.dpms_activeoff,
    }

# 改造检查函数为统一结构返回
def check_raw_data(raw_edid_hex):
    header = raw_edid_hex[:16].lower()
    a = "00ffffffffffff00"
    result = {
        "step": "头部格式检查",
        "expected": "00ffffffffffff00",
        "actual": header,
        "status": (header == a),
        "message": f"预期: {a} | 实际: {header}"
    }
    return result

def check_ninth_byte_non_zero(raw_edid_hex):
    try:
        edid_bytes = bytes.fromhex(raw_edid_hex)
    except ValueError:
        return {
            "step": "第九字节非零检查",
            "status": False,
            "message": "输入的HEX字符串无效。"
        }
    
    if len(edid_bytes) < 9:
        return {
            "step": "第九字节非零检查",
            "status": False,
            "message": "EDID数据不足9个字节。"
        }
    
    ninth_byte = edid_bytes[8]
    result = {
        "step": "第九字节非零检查",
        "value": f"{ninth_byte:02X}",
        "status": (ninth_byte != 0x00),
        "message": f"第9字节值应为非00（当前值: {ninth_byte:02X})"
    }
    return result

def validate_checksum(raw_edid_hex):
    try:
        edid_bytes = bytes.fromhex(raw_edid_hex)
    except ValueError:
        return {
            "step": "校验和验证",
            "status": False,
            "message": "输入的HEX字符串无效。"
        }
    
    if len(edid_bytes) != 128:
        return {
            "step": "校验和验证",
            "status": False,
            "message": "EDID长度必须为128字节。"
        }
    
    checksum = sum(edid_bytes) % 256
    result = {
        "step": "校验和验证",
        "computed": f"{checksum:02X}",
        "status": (checksum == 0),
        "message": f"校验和应为0x00（当前值: {checksum:02X})"
    }
    return result

def validate_edid(raw_hex):
    """独立的验证函数，返回完整验证结果"""
    steps = []
    
    # 添加基础长度检查
    if len(raw_hex) != 256:
        steps.append({
            "step": "EDID长度验证",
            "status": False,
            "message": f"EDID长度必须为256个十六进制字符（实际长度: {len(raw_hex)}）"
        })
        return {"status": False, "steps": steps}
    
    # 执行其他验证步骤
    steps.append(check_raw_data(raw_hex))
    steps.append(check_ninth_byte_non_zero(raw_hex))
    checksum_step = validate_checksum(raw_hex)
    steps.append(checksum_step)
    
    status = all(step["status"] for step in steps) if steps else False
    
    # 添加最终总结步骤
    steps.append({
        "step": "综合验证结果",
        "status": status,
        "message": f"验证{'通过' if status else '失败'}"
    })
    
    return {"status": status, "steps": steps}

def main():
    # 创建参数解析器
    parser = argparse.ArgumentParser(description="EDID信息查看器")
    # 添加--auto参数，布尔类型
    parser.add_argument('--auto', action='store_true', help='自动执行验证', default=False)
    # 添加--export参数，字符串类型
    parser.add_argument('--export', action='store_true', help='导出验证结果', default=False)
    
    # 解析命令行参数
    args = parser.parse_args()
    
    gui = EdidGUI(auto=args.auto, export=args.export)
    gui.run()

if __name__ == "__main__":
    main()